//
// CharStringTest.c - MrsWatson
// Copyright (c) 2016 Teragon Audio. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//

#include "base/CharString.h"

#include "unit/TestRunner.h"

static char *const TEST_STRING = "test string";
static char *const TEST_STRING_CAPS = "TEST STRING";
static char *const OTHER_TEST_STRING = "other test string";

static int _testNewCharString(void) {
  CharString c = newCharString();
  assertSizeEquals(kCharStringLengthDefault, c->capacity);
  assertCharStringEquals(EMPTY_STRING, c);
  freeCharString(c);
  return 0;
}

static int _testNewCharStringWithCapacity(void) {
  size_t testSize = 123;
  CharString c = newCharStringWithCapacity(testSize);
  assertSizeEquals(testSize, c->capacity);
  assertCharStringEquals(EMPTY_STRING, c);
  freeCharString(c);
  return 0;
}

static int _testNewObjectWithNullCString(void) {
  CharString c = newCharStringWithCString(NULL);
  assertSizeEquals(kCharStringLengthDefault, c->capacity);
  assertCharStringEquals(EMPTY_STRING, c);
  freeCharString(c);
  return 0;
}

static int _testNewObjectWithEmptyCString(void) {
  CharString c = newCharStringWithCString(EMPTY_STRING);
  assertSizeEquals(kCharStringLengthDefault, c->capacity);
  assertCharStringEquals(EMPTY_STRING, c);
  freeCharString(c);
  return 0;
}

static int _testClearCharString(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  charStringClear(c);
  assertSizeEquals(kCharStringLengthDefault, c->capacity);
  assertCharStringEquals(EMPTY_STRING, c);
  freeCharString(c);
  return 0;
}

static int _testCopyToCharString(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  assertSizeEquals(kCharStringLengthDefault, c->capacity);
  assertCharStringEquals(TEST_STRING, c);
  freeCharString(c);
  return 0;
}

static int _testCopyCharStrings(void) {
  CharString c, c2;
  c = newCharString();
  c2 = newCharString();
  charStringCopyCString(c, TEST_STRING);
  charStringCopy(c2, c);
  assertCharStringEquals(c2->data, c);
  freeCharString(c);
  freeCharString(c2);
  return 0;
}

static int _testIsEmptyStringEmpty(void) {
  CharString c = newCharString();
  assert(charStringIsEmpty(c));
  freeCharString(c);
  return 0;
}

static int _testIsNullEmptyString(void) {
  assert(charStringIsEmpty(NULL));
  return 0;
}

static int _testIsRegularStringNotEmpty(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  assertFalse(charStringIsEmpty(c));
  freeCharString(c);
  return 0;
}

static int _testAppendCharStrings(void) {
  CharString a = newCharStringWithCString("a");
  CharString b = newCharStringWithCString("b");
  charStringAppend(a, b);
  assertCharStringEquals("ab", a);
  freeCharString(a);
  freeCharString(b);
  return 0;
}

static int _testAppendCharStringsOverCapacity(void) {
  CharString a = newCharStringWithCString("a");
  CharString b = newCharStringWithCString("1234567890");
  assertSizeEquals((size_t)2, a->capacity);
  charStringAppend(a, b);
  assertCharStringEquals("a1234567890", a);
  assertSizeEquals((size_t)12, a->capacity);
  freeCharString(a);
  freeCharString(b);
  return 0;
}

static int _testCharStringEqualsSameString(void) {
  CharString c, c2;
  c = newCharString();
  c2 = newCharString();
  charStringCopyCString(c, TEST_STRING);
  charStringCopyCString(c2, TEST_STRING);
  assert(charStringIsEqualTo(c, c2, false));
  freeCharString(c);
  freeCharString(c2);
  return 0;
}

static int _testCharStringDoesEqualDifferentString(void) {
  CharString c, c2;
  c = newCharString();
  c2 = newCharString();
  charStringCopyCString(c, TEST_STRING);
  charStringCopyCString(c2, OTHER_TEST_STRING);
  assertFalse(charStringIsEqualTo(c, c2, false));
  freeCharString(c);
  freeCharString(c2);
  return 0;
}

static int _testCharStringEqualsSameStringInsensitive(void) {
  CharString c, c2;
  c = newCharString();
  c2 = newCharString();
  charStringCopyCString(c, TEST_STRING);
  charStringCopyCString(c2, TEST_STRING_CAPS);
  assert(charStringIsEqualTo(c, c2, true));
  freeCharString(c);
  freeCharString(c2);
  return 0;
}

static int _testCharStringDoesNotEqualSameStringInsensitive(void) {
  CharString c, c2;
  c = newCharString();
  c2 = newCharString();
  charStringCopyCString(c, TEST_STRING);
  charStringCopyCString(c2, TEST_STRING_CAPS);
  assertFalse(charStringIsEqualTo(c, c2, false));
  freeCharString(c);
  freeCharString(c2);
  return 0;
}

static int _testCharStringEqualsNull(void) {
  CharString c = newCharString();
  assertFalse(charStringIsEqualTo(c, NULL, false));
  freeCharString(c);
  return 0;
}

static int _testCharStringEqualsSameCString(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  assert(charStringIsEqualToCString(c, TEST_STRING, false));
  freeCharString(c);
  return 0;
}

static int _testCharStringNotEqualToDifferentCString(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  assertFalse(charStringIsEqualToCString(c, OTHER_TEST_STRING, false));
  freeCharString(c);
  return 0;
}

static int _testCharStringEqualsSameCStringInsensitive(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  assert(charStringIsEqualToCString(c, TEST_STRING_CAPS, true));
  freeCharString(c);
  return 0;
}

static int _testCharStringNotEqualsCStringInsensitive(void) {
  CharString c = newCharString();
  charStringCopyCString(c, TEST_STRING);
  assertFalse(charStringIsEqualToCString(c, TEST_STRING_CAPS, false));
  freeCharString(c);
  return 0;
}

static int _testCharStringEqualsCStringNull(void) {
  CharString c = newCharString();
  assertFalse(charStringIsEqualToCString(c, NULL, false));
  freeCharString(c);
  return 0;
}

static int _testIsLetter(void) {
  CharString c = newCharStringWithCString("a");
  assert(charStringIsLetter(c, 0));
  freeCharString(c);
  return 0;
}

static int _testIsNotLetter(void) {
  CharString c = newCharStringWithCString("0");
  assertFalse(charStringIsLetter(c, 0));
  freeCharString(c);
  return 0;
}

static int _testIsNumber(void) {
  CharString c = newCharStringWithCString("0");
  assert(charStringIsNumber(c, 0));
  freeCharString(c);
  return 0;
}

static int _testIsNotNumber(void) {
  CharString c = newCharStringWithCString("a");
  assertFalse(charStringIsNumber(c, 0));
  freeCharString(c);
  return 0;
}

static int _testSplitString(void) {
  CharString c = newCharStringWithCString("abc,def,ghi,");
  LinkedList l = charStringSplit(c, ',');
  CharString *items = NULL;

  assertNotNull(l);
  assertIntEquals(3, linkedListLength(l));
  items = (CharString *)linkedListToArray(l);
  assertNotNull(items);
  assertCharStringEquals("abc", items[0]);
  assertCharStringEquals("def", items[1]);
  assertCharStringEquals("ghi", items[2]);

  freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString);
  freeCharString(c);
  free(items);
  return 0;
}

static int _testSplitStringWithoutDelimiter(void) {
  const char *expected = "abcdefg";
  CharString c = newCharStringWithCString(expected);
  CharString c2 = NULL;
  LinkedList l = charStringSplit(c, ',');

  assertNotNull(l);
  assertIntEquals(1, linkedListLength(l));
  c2 = l->item;
  assertCharStringEquals(expected, c2);

  freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString);
  freeCharString(c);
  return 0;
}

static int _testSplitStringNULLDelimiter(void) {
  CharString c = newCharStringWithCString("abcdefh");
  LinkedList l = charStringSplit(c, 0);
  assertIsNull(l);
  freeCharString(c);
  return 0;
}

static int _testSplitStringEmptyString(void) {
  CharString c = newCharString();
  LinkedList l = charStringSplit(c, ',');
  assertNotNull(l);
  assertIntEquals(0, linkedListLength(l));
  freeCharString(c);
  freeLinkedList(l);
  return 0;
}

// This function is not technically public, but we test against it instead of
// the public version in order to set a shorter line length. This makes test
// cases much easier to construct.
extern boolByte _charStringWrap(const char *srcString, char *destString,
                                size_t destStringSize, int indentSize,
                                int lineLength);

static int _testWrapNullSourceString(void) {
  assertFalse(charStringWrap(NULL, 0));
  return 0;
}

static int _testWrapString(void) {
  CharString src = newCharStringWithCString("1234 6789 bcde 01");
  // Create dest string the same way as in wrapString(), cheap I know...
  CharString dest = newCharStringWithCapacity(src->capacity * 2);
  _charStringWrap(src->data, dest->data, dest->capacity, 0, 0x10);
  assertCharStringEquals("1234 6789 bcde\n01", dest);
  freeCharString(src);
  freeCharString(dest);
  return 0;
}

static int _testWrapStringWithIndent(void) {
  CharString src = newCharStringWithCString("1234 6789 bcde 01");
  // Create dest string the same way as in wrapString(), cheap I know...
  CharString dest = newCharStringWithCapacity(src->capacity * 2);
  _charStringWrap(src->data, dest->data, dest->capacity, 1, 0xe);
  assertCharStringEquals(" 1234 6789\n bcde 01", dest);
  freeCharString(src);
  freeCharString(dest);
  return 0;
}

static int _testWrapStringLongerThanLine(void) {
  CharString src = newCharStringWithCString("123456789abcdef12");
  // Create dest string the same way as in wrapString(), cheap I know...
  CharString dest = newCharStringWithCapacity(src->capacity * 2);
  _charStringWrap(src->data, dest->data, dest->capacity, 0, 0xf);
  assertCharStringEquals("123456789abcde-\nf12", dest);
  freeCharString(src);
  freeCharString(dest);
  return 0;
}

static int _testFreeNullCharString(void) {
  freeCharString(NULL);
  return 0;
}

TestSuite addCharStringTests(void);
TestSuite addCharStringTests(void) {
  TestSuite testSuite = newTestSuite("CharString", NULL, NULL);

  addTest(testSuite, "NewObject", _testNewCharString);
  addTest(testSuite, "NewObjectWithCapacity", _testNewCharStringWithCapacity);
  addTest(testSuite, "NewObjectWithNullCString", _testNewObjectWithNullCString);
  addTest(testSuite, "NewObjectWithEmptyCString",
          _testNewObjectWithEmptyCString);

  addTest(testSuite, "ClearString", _testClearCharString);
  addTest(testSuite, "CopyToCharString", _testCopyToCharString);
  addTest(testSuite, "CopyCharStrings", _testCopyCharStrings);
  addTest(testSuite, "EmptyStringIsEmpty", _testIsEmptyStringEmpty);
  addTest(testSuite, "NullIsEmpty", _testIsNullEmptyString);
  addTest(testSuite, "RegularStringIsNotEmpty", _testIsRegularStringNotEmpty);

  addTest(testSuite, "AppendCharStrings", _testAppendCharStrings);
  addTest(testSuite, "AppendCharStringsOverCapacity",
          _testAppendCharStringsOverCapacity);

  addTest(testSuite, "EqualsSameString", _testCharStringEqualsSameString);
  addTest(testSuite, "DoesNotEqualDifferentString",
          _testCharStringDoesEqualDifferentString);
  addTest(testSuite, "EqualsSameStringWithCaseInsensitive",
          _testCharStringEqualsSameStringInsensitive);
  addTest(testSuite, "DoesNotEqualSameStringWithDifferentCase",
          _testCharStringDoesNotEqualSameStringInsensitive);
  addTest(testSuite, "EqualsNull", _testCharStringEqualsNull);

  addTest(testSuite, "EqualsSameCString", _testCharStringEqualsSameCString);
  addTest(testSuite, "DoesNotEqualDifferentCString",
          _testCharStringNotEqualToDifferentCString);
  addTest(testSuite, "EqualsSameCStringWithCaseInsensitive",
          _testCharStringEqualsSameCStringInsensitive);
  addTest(testSuite, "DoesNotEqualSameCStringWithDifferentCase",
          _testCharStringNotEqualsCStringInsensitive);
  addTest(testSuite, "EqualsCStringNull", _testCharStringEqualsCStringNull);

  addTest(testSuite, "IsLetter", _testIsLetter);
  addTest(testSuite, "IsNotLetter", _testIsNotLetter);
  addTest(testSuite, "IsNumber", _testIsNumber);
  addTest(testSuite, "IsNotNumber", _testIsNotNumber);

  addTest(testSuite, "SplitString", _testSplitString);
  addTest(testSuite, "SplitStringWithoutDelimiter",
          _testSplitStringWithoutDelimiter);
  addTest(testSuite, "SplitStringNULLDelimiter", _testSplitStringNULLDelimiter);
  addTest(testSuite, "SplitStringEmptyString", _testSplitStringEmptyString);

  addTest(testSuite, "WrapNullSourceString", _testWrapNullSourceString);
  addTest(testSuite, "WrapString", _testWrapString);
  addTest(testSuite, "WrapStringWithIndent", _testWrapStringWithIndent);
  addTest(testSuite, "WrapStringLongerThanLine", _testWrapStringLongerThanLine);

  addTest(testSuite, "FreeNullCharString", _testFreeNullCharString);

  return testSuite;
}
